﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Curchy.MetablogCMS.Models;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;

namespace Curchy.MetablogCMS.Persistance
{
    public class MetablogPersistance
    {
        #region Create Post

        public static string AddPost(string blogid, string username, Post post)
        {
            if (post.wp_slug == string.Empty)
            {
                if (post.categories != null && post.categories.Length > 0)
                    post.wp_slug = SanitizeUrl(post.categories[0] + "/" + post.title);
                else
                    post.wp_slug = SanitizeUrl(post.title);
            }

            Page page = new Page();
            page.PageId = Guid.NewGuid();
            page.PostId = Guid.NewGuid();
            page.PageSetId = blogid;
            if (!string.IsNullOrEmpty(post.userid))
                page.UserId = post.userid;
            else
                page.UserId = username;
            if (post.dateCreated != null && post.dateCreated != DateTime.MinValue)
                page.CreationDate = post.dateCreated.ToLocalTime();
            else
                page.CreationDate = DateTime.Now;
            page.Categories = string.Join(",", post.categories);            
            page.Description = post.description;
            page.Permalink = "metablog/permalink/" + page.PostId.ToString(); ;
            page.Slug = post.wp_slug;
            page.Title = post.title;
            page.Version = 1;

            using (Entities e = new Entities())
            {
                e.Pages.AddObject(page);
                e.SaveChanges();

                return page.PostId.ToString();
            }
        }

        #endregion

        #region Read - Post

        public static Post GetPost(Guid guid)
        {
            using (Entities e = new Entities())
            {
                return ConvertToPost(e.Pages.Where(p => p.PostId == guid).OrderByDescending(p=>p.Version).FirstOrDefault());
            }
        }

        public static Page GetPage(string slug)
        {
            using (Entities e = new Entities())
            {
                return e.Pages.Where(p => p.Slug == slug && (p.UpdateDate == null || p.UpdateDate< DateTime.Now) && p.CreationDate < DateTime.Now).OrderByDescending(p => p.Version).FirstOrDefault();
            }
        }

        public static List<Post> GetPosts(string blogid, int number)
        {
            using (Entities e = new Entities())
            {
                return e.ExecuteStoreQuery<Page>(@"                    
                    SELECT 
	                    p.*
                    FROM 
	                    Page p 
	                    INNER JOIN 
	                    (
		                    SELECT 
			                    Slug, Max(Version) AS MaxVersion
		                    FROM 
			                    Page 
		                    GROUP BY 
			                    Slug
	                    ) AS m ON p.Slug = m.Slug AND p.Version = m.MaxVersion
                    ORDER BY
	                    Coalesce(p.UpdateDate, p.CreationDate) DESC
                ").Take(number).ToList().ConvertAll(p=>ConvertToPost(p));
                        
            }
        }

        #endregion        

        #region Update - Post

        public static void UpdatePost(string postid, string username, Post post)
        {
            Guid postId = new Guid(postid.ToString());

            Page oldPage;
            using (Entities e = new Entities())
                oldPage = e.Pages.OrderByDescending(p=>p.Version).FirstOrDefault(p => p.PostId == postId);

            Page page = new Page();
            page.PageId = Guid.NewGuid();
            page.PostId = oldPage.PostId;
            page.PageSetId = oldPage.PageSetId;
            page.Slug = oldPage.Slug;
            page.CreationDate = oldPage.CreationDate;
            page.Version = oldPage.Version.HasValue ? oldPage.Version + 1 : 2;
            page.AggCount = oldPage.AggCount;
            page.HitCount = oldPage.HitCount;
            page.Permalink = oldPage.Permalink;

            page.Title = post.title;
            page.Description = post.description;
            if (string.IsNullOrEmpty(post.userid))
                page.UserId = username;
            else
                page.UserId = post.userid;
                        
            if (post.dateCreated != null && post.dateCreated != DateTime.MinValue)
                page.UpdateDate = post.dateCreated.ToLocalTime();                
            else
                page.UpdateDate = DateTime.Now;

            if (post.categories == null || post.categories.Length==0)
                page.Categories = oldPage.Categories;
            else
                page.Categories = string.Join(",", post.categories);

            using (Entities e = new Entities())
            {
                e.Pages.AddObject(page);
                e.SaveChanges();
            }
        }

        #endregion

        #region Delete
        
        public static void RemovePost(Guid guid)
        {
            using (Entities e = new Entities())
            {
                e.Pages.DeleteObject(e.Pages.Single(p => p.PageId == guid));
                e.SaveChanges();
            }
        }

        #endregion

        #region Util - Post

        private static Post ConvertToPost(Page page)
        {
            if (page == null) return default(Post);

            Post p = new Post();
            if (!string.IsNullOrEmpty(page.Categories))
                p.categories = page.Categories.Split(',');
            p.dateCreated = page.CreationDate;
            p.description = page.Description;
            p.permalink = page.Permalink;
            p.postid = page.PostId.ToString();
            p.title = page.Title;
            p.userid = page.UserId;
            p.wp_slug = page.Slug;
            return p;
        }

        private static string SanitizeUrl(string s)
        {
            string url = Regex.Replace(s, @"\s+", ".");
            url = url.Normalize(NormalizationForm.FormD);
            url = Regex.Replace(url, @"[^A-Za-z0-9/]", string.Empty);

            StringBuilder sb = new StringBuilder();

            for (int ich = 0; ich < url.Length; ich++)
            {
                UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(url[ich]);
                if (uc != UnicodeCategory.NonSpacingMark)
                {
                    sb.Append(url[ich]);
                }
            }

            int increment = 0;
            string baseUId = sb.ToString();
            string suffix = string.Empty;

            if (baseUId.StartsWith("/"))
                baseUId = baseUId.Substring(1);

            while (PageSlugExists(baseUId + suffix))
            {
                increment++;
                if (string.IsNullOrEmpty(baseUId))
                    baseUId += ".";

                suffix = increment.ToString();
            }

            return baseUId + suffix;
        }

        private static bool PageSlugExists(string slug)
        {
            using (Entities e = new Entities())
                return e.Pages.Where(p => p.Slug == slug).Count() > 0;
        }
        #endregion

        public static Medium GetMedium(Guid id)
        {
            using (Entities e = new Entities())
                return e.Media.SingleOrDefault(p=>p.MediaId == id);
        }

        public static Guid AddMedia(string blogid, MediaObject mediaObject, Guid mediaId, string url)
        {
            Medium m = new Medium();
            m.MediaId = mediaId;
            m.Name = mediaObject.name;
            m.Type = mediaObject.type;
            m.CreationDate = DateTime.Now;
            m.Content = mediaObject.bits;
            m.BlogId = int.Parse(blogid);
            m.Url = url;

            using (Entities e = new Entities())
            {
                e.Media.AddObject(m);
                e.SaveChanges();
            }

            return m.MediaId;
        }
    }
}